use std::error::Error;
use std::fs;
use std::env;

pub struct Config{
    query:String,
    path:String,
    ignore_case:bool
}
impl Config{
    /// 使用Result作为返回，可恢复异常。让调用者去处理异常，有异常直接提前终止
    /// 一般来说new函数不会失败。build可能会失败
    /// 需要一个实现了Iterator的属性，同时Item类型为String的可变参数
    pub fn build(
        mut args:impl Iterator<Item = String>) -> Result<Config,&'static str>{
        args.next(); // 跳过程序名
        // next有值就返回值否则None
        let query = match args.next() {
            Some(q)=>q,
            None=> return Err("query is missing")
        };
        let path = match args.next() {
            Some(p)=>p,
            None=> return Err("path is missing")
        };
        // env::var 返回Result is_ok查看是否是ok，不关心ok的值是什么
        let ignore_case = env::var("IGNORE_CASE").is_ok();
        Ok(Config{
            query,
            path,
            ignore_case
        })
    }
}
/// 使用Result作为返回，可恢复异常。让调用者去处理异常，有异常直接提前终止。直接使用“?”，遇到异常直接终止
pub fn run(config: Config) -> Result<(),Box<dyn Error>>{
    let content = fs::read_to_string(config.path)?;
    let match_lines = if config.ignore_case {
        search_insensitive(&config.query,&content)
    }else {
        search(&config.query,&content)
    };
    // 标准输出 重定向到文件储存
    println!("matched line is: ");
    for matched_line in match_lines{
        println!("{matched_line}");
    }
    Ok(())
}
/// 搜索文本
/// 因为是2个入参一个出参+非方法。需要指定生命周期参数
pub fn search<'a>(query:&str,content:&'a str) -> Vec<&'a str>{
    content
        .lines()
        .filter(|line| line.contains(query))
        .collect()
}
/// 大小写不敏感
pub fn search_insensitive<'a>(query:&str,content:&'a str) -> Vec<&'a str>{
    let lower_query = query.to_lowercase();
    content.lines().filter(|line|line.to_lowercase().contains(lower_query)).collect()
}
/// 编写单元测试
#[cfg(test)]
mod test{
    use super::*;
    #[test]
    fn case_sensitive(){
        let query = "duct";
        // " + \ 告诉编译器不要在字符串字面量前加换行符
        let contents = "\
Rust:
safe, fast, productive.
Duct three.";
        assert_eq!(vec!["safe, fast, productive."],search(query,contents))
    }
    #[test]
    fn case_insensitive(){
        let query = "RusT";
        let contents = "\
Rust:
safe, fast, productive.
Pick three.
Trust me.";
        assert_eq!(vec!["Rust:","Trust me."],search_insensitive(query,contents));
    }

}